<?php

namespace app\web\controller;

use app\web\Base;
use \think\Db;

class Wxpay extends Base
{
    /**
     * 微信支付参数配置
     *
     * @var array
     */
    private $config = array(
        'appid' => "wx947abdfbf1e7cc2c",
        'mch_id' => "1556616021",
        'api_key' => "358lxpbmqCIBUjxyuaafqUQC4WZvCU0x",
        'notify_url' => 'https://new.hamm.cn/web/wxpay/callback',

    );

    public function _empty()
    {
        $this->login();
        return $this->show();
    }
    /**
     * 支付页面
     *
     * @return void
     */
    public function pay()
    {
        $this->login();
        if ($this->request->isAjax()) {
            if (empty(input("money"))) {
                return jerr("请输入金额后支付！");
            }
            $money = floatval(input("money"));
            $money = number_format($money, 2);
            if ($money <= 0) {
                return jerr("输入的金额不正确！");
            }
            $order_id = 1;
            $out_trade_no = date('YmdHis') . rand(10000000, 99999999);
            $ret = $this->wxpay($this->wechat["wechat_openid"], "消费 ￥" . $money . "元", $order_id, $out_trade_no, $money * 100);
            return $ret;
        }
        return $this->show();
    }
    /**
     * 微信支付下单
     *
     * @param [type] $openid
     * @param [type] $title
     * @param [type] $orderid
     * @param [type] $out_trade_no
     * @param [type] $total_fee
     * @return void
     */
    protected function wxpay($openid, $title, $orderid, $out_trade_no, $total_fee)
    {
        $this->login();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $data["appid"]            = $this->config["appid"];
        $data["openid"]            = $openid;
        $data["body"]             = $title;
        $data["mch_id"]           = $this->config['mch_id'];
        $data["nonce_str"]        = $this->createNoncestr();
        $data["notify_url"]       = $this->config["notify_url"];
        $data['trade_type']       = 'JSAPI';
        $data["total_fee"]        = $total_fee;
        $data["out_trade_no"]     = $out_trade_no;
        $data["spbill_create_ip"] = $this->get_client_ip();
        $sign                     = $this->getSign($data);
        $data["sign"]             = $sign;

        $xml      = $this->arrayToXml($data);
        $response = $this->postXmlCurl($xml, $url);
        $response = $this->xmlToArray($response);
        if ($response['return_code'] == 'FAIL') {
            return jerr( $response['return_msg']);
        }
        $response = $this->two_sign($response, $data["nonce_str"]);
        //返回数据 TODO 修改订单状态为支付中
        return jok('success', $response);
    }
    /**
     * 微信支付回调callback
     *
     * @return void
     */
    public function callback()
    {
        $xml_data = file_get_contents('php://input');
        $data = $this->xmlToArray($xml_data);
        $sign = $data['sign'];
        unset($data['sign']);
        if ($sign == $this->getSign($data)) {
            if ($data['result_code'] == 'SUCCESS') {
                $order_trade = $data['out_trade_no'];
                //TODO 修改订单状态
                echo "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
            }
        }
    }
    /**
     * 微信支付退款
     *
     * @param [type] $pay_sn
     * @param [type] $total_fee
     * @param [type] $refund_fee
     * @return void
     */
    protected function wxrefundOrder($pay_sn, $total_fee, $refund_fee)
    {
        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";

        $data["appid"]            = $this->config["appid"];
        $data["mch_id"]           = $this->config['mch_id'];
        $data["nonce_str"]        = $this->createNoncestr();
        $data['out_trade_no']     = $pay_sn;
        $data['out_refund_no']    = $pay_sn;
        $data["total_fee"]        = intval($total_fee * 100);
        $data["refund_fee"]       = intval($refund_fee * 100);
        $sign                     = $this->getSign($data);
        $data["sign"]             = $sign;

        $xml      = $this->arrayToXml($data);
        $response = $this->postXmlCurl($xml, $url);
        $response = $this->xmlToArray($response);
        if ($response['return_code'] == 'SUCCESS' && $response['refund_id']) {
            return ['code' => 1, 'refund_id'    => $response['refund_id']];
        } else {
            return ['code' => 0, 'err_code_des' => $response['err_code_des']];
        }
    }

    /**
     * 订单记录
     *
     * @param [type] $orderid
     * @param [type] $unionid
     * @param [type] $title
     * @param [type] $price
     * @param [type] $user_money
     * @return void
     */
    protected function order_record($orderid, $unionid, $title, $price, $user_money)
    {
        $record['orderid'] = $orderid;
        $record['unionid'] = $unionid;
        $record['title'] = $title;
        $record['price'] = $price;
        $record['money'] = $user_money;
        $record['addtime'] = time();
        Db::name('order_record')->insert($record);
    }

    /**
     * 微信支付二次签名
     *
     * @param [type] $response
     * @param [type] $nonce_str
     * @return void
     */
    protected function two_sign($response = NULL, $nonce_str)
    {
        if ($response != NULL && $response['return_code'] != 'SUCCESS') {
            return [];
        } else {
            //接收微信返回的数据,传给APP!
            $arr = array(
                'appId'     => $this->config["appid"],
                'timeStamp' => time() . "",
                'nonceStr'  => $nonce_str,
                'package'   => 'prepay_id=' . $response['prepay_id'],
                'signType'  => 'MD5',
            );
            //第二次生成签名
            $sign = $this->getSign($arr);
            $arr['paySign'] = $sign;
            return $arr;
        }
    }
    /**
     * 微信支付签名
     *
     * @param [type] $Obj
     * @return void
     */
    protected function getSign($Obj)
    {
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        // echo '【string1】：'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String . "&key=" . $this->config['api_key'];
        // echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        // echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        // echo "【result】 ".$result_."</br>";
        return $result_;
    }

    /**
     * 产生随机字符串
     *
     * @param integer $length
     * @return void
     */
    protected function createNoncestr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 获取当前服务器的IP
     *
     * @return void
     */
    protected function get_client_ip()
    {
        if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
        } else {
            $cip = "unknown";
        }
        return $cip;
    }

    /**
     * 数组转xml
     *
     * @param [type] $arr
     * @return void
     */
    protected function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }


    /**
     * 将xml转为array
     *
     * @param [type] $xml
     * @return void
     */
    protected function xmlToArray($xml)
    {
        //将XML转为array       
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }


    /**
     * 以post方式提交xml到对应的接口url
     *
     * @param [type] $xml
     * @param [type] $url
     * @param integer $second
     * @return void
     */
    protected function postXmlCurl($xml, $url, $second = 30)
    {
        //初始化curl       
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        //退款证书
        curl_setopt($ch, CURLOPT_SSLCERT, '/cert/wx/apiclient_cert.pem');
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        //退款证书
        curl_setopt($ch, CURLOPT_SSLKEY, '/cert/wx/apiclient_key.pem');

        //运行curl
        $data = curl_exec($ch);
        //返回结果

        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error" . "<br>";
            curl_close($ch);
            return false;
        }
    }

    /**
     * 格式化参数，签名过程需要使用
     *
     * @param [type] $paraMap
     * @param [type] $urlencode
     * @return void
     */
    protected function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar;
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }
}
